package com.xyy.saas.payment.adpater.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.adpater.jd.common.JDConstants;
import com.xyy.saas.payment.adpater.jd.service.JDAggregateAdpaterService;
import com.xyy.saas.payment.adpater.jd.vo.aggrate.*;
import com.xyy.saas.payment.cashier.core.vo.ChannelLoginVO;
import com.xyy.saas.payment.channel.login.core.bo.ChannelLoginResultBO;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.OrderClosedBo;
import com.xyy.saas.payment.cores.bo.PayBo;
import com.xyy.saas.payment.cores.bo.PrepayBo;
import com.xyy.saas.payment.cores.bo.QueryResultBo;
import com.xyy.saas.payment.cores.enums.ChannelEnum;
import com.xyy.saas.payment.cores.enums.JDPayTradeStatusEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.dao.model.PayResultOrder;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.merchant.core.dto.QueryTransactionStatusDto;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.dto.OrderClosedDto;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.dto.PrepayDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.HttpClientUtil;
import com.xyy.saas.payment.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * 京东支付
 */
@Service
@Slf4j
public class JDAggrateAdapter implements PaymentAdapter {
    @Autowired
    private PrePayOrderService prePayOrderService;
    @Autowired
    private JDAggregateAdpaterService jdAggregateAdpaterService;
    @Autowired
    private PayResultOrderService payResultOrderService;

    @Override
    public ChannelLoginResultBO login(ChannelLoginVO channelLoginVO) throws PaymentException {
        String logPrefix = "jdaggregate_wx_login_" + channelLoginVO.getPayNo();
        log.info("{} login : {}", logPrefix, JSON.toJSONString(channelLoginVO));
        if (channelLoginVO.getChannel().equals(ChannelEnum.JDAGGREGATE_WX.getCode())) {
            return loginWx(logPrefix, channelLoginVO);
        }
        log.error("{} 不支持的京东聚合支付渠道类型:{}", logPrefix, JSON.toJSONString(channelLoginVO));
        throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
    }
    private static final String WX_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
    private ChannelLoginResultBO loginWx(String logPrefix, ChannelLoginVO channelLoginVO) throws PaymentException {
        String appId = JDConstants.aggregateWxAppID(logPrefix, channelLoginVO.getBusinessType());
        String secret = JDConstants.aggregateWxSecret(logPrefix, channelLoginVO.getBusinessType());
        ChannelLoginResultBO bo = new ChannelLoginResultBO();
        String url = String.format(WX_LOGIN_URL + "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", appId, secret, channelLoginVO.getAuthCode());
        try {
            String response = HttpClientUtil.xyyHttpGetRequest(url);
            if(response == null) {
                log.error("{} JDAggrateAdapter#login failed url:{} ", logPrefix, url);
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
            Map<String, String> jsonRes = JSONUtils.parse2Map(response, String.class, String.class);
            if(jsonRes == null) {
                log.error("{} JDAggrateAdapter#login parse response error url:{} response:{}", logPrefix, url, response);
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
            log.info("{} JDAggrateAdapter#login url:{} result:{}", logPrefix, url, jsonRes);
            bo.setChannelUserId(jsonRes.get("openid"));
            return bo;
        }
        catch(Exception e) {
            log.error("{} JDAggrateAdapter#login error url:{}", logPrefix, url, e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    /**
     * 创建预支付单
     */
    @Override
    public PrepayBo prepay(PrepayDto prepayDto) throws PaymentException {
        log.info("JDAggrateAdapter#prepay payDto={}", JSONUtils.toJSON(prepayDto));
        return prePayOrderService.prepayOrderByPrepayDto(prepayDto);
    }

    /**
     * 支付
     */
    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        payDto.setMap(null == payDto.getMap() ? new HashMap<>() : payDto.getMap());
        String logPrefix = "jdAggrateOrderCreate " + payDto.getPayNo();
        log.info("{} JDAggrateAdapter#pay payDto={}", logPrefix, JSON.toJSONString(payDto));
        // 拼装请求参数
        JdPayAggregateCreateOrderVo jdPayAggregateCreateOrderVo = new JdPayAggregateCreateOrderVo();
        jdPayAggregateCreateOrderVo.setMerchantNo(JDConstants.aggregateMerchant(logPrefix,payDto.getChannel(), payDto.getBusinessType()));
        jdPayAggregateCreateOrderVo.setOutTradeNo(payDto.getPayNo());
        jdPayAggregateCreateOrderVo.setTradeAmount(String.valueOf(payDto.getAmount()));
        //bizUserId 为业务侧accountId
        Object bizUserId = payDto.getMap().get("bizUserId");
        if (bizUserId != null) {
            jdPayAggregateCreateOrderVo.setUserId((String) bizUserId);
        } else {
            jdPayAggregateCreateOrderVo.setUserId(payDto.getPayerId());
        }

        jdPayAggregateCreateOrderVo.setTradeSubject(payDto.getSubject());
        jdPayAggregateCreateOrderVo.setTradeRemark(payDto.getBody());
        if (payDto.getMap().containsKey("timeExpire")) {
            jdPayAggregateCreateOrderVo.setTradeExpiryTime(String.valueOf(DateUtil.getTimeDiffMinute(new Date(), DateUtil.parase(payDto.getMap().get("timeExpire").toString(), DateUtil.YYYYMMDDHHMMSS))));
        }
        jdPayAggregateCreateOrderVo.setUserIp(payDto.getMap().containsKey("client_ip") ? payDto.getMap().get("client_ip").toString() : "");
        jdPayAggregateCreateOrderVo.setPageBackUrl(payDto.getMap().containsKey("frontSkipUrl") ? payDto.getMap().get("frontSkipUrl").toString() : "");
        if (null != payDto.getCreateTime()) {
            jdPayAggregateCreateOrderVo.setCreateDate(DateUtil.format(payDto.getCreateTime(), DateUtil.YYYYMMDDHHMMSS));
        }
        jdPayAggregateCreateOrderVo.transType(logPrefix, payDto);

        // 发起支付请求
        JdPayAggregateCreateOrderResponse jdPayAggregateCreateOrderResponse;
        try {
            jdPayAggregateCreateOrderResponse = jdAggregateAdpaterService.aggregateCreateOrder(logPrefix, jdPayAggregateCreateOrderVo);
        } catch (PaymentException ep) {
            log.error("{} JDAggrateAdapter#pay_error payDto={} error : {}", logPrefix, JSON.toJSONString(payDto), ep);
            throw ep;
        } catch (Exception e) {
            log.error("{} JDAggrateAdapter#pay_error payDto={} error : {}", logPrefix, JSON.toJSONString(payDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
        }
        //{"outTradeNo":"1920732560736915504","payInfo":"{\"tradeId\":\"2025050922001489401434864095\"}","resultCode":"0000","resultDesc":"成功","tradeNo":"202505091449032017090446502929"}
        //{\"tradeId\":\"2025050922001489401434864095\"}
        // 返回数据
        PayBo payBo = new PayBo();
        payBo.setPayNO(payDto.getPayNo());
        if (StringUtils.isNotBlank(jdPayAggregateCreateOrderResponse.getPayInfo())) {
            JSONObject payInfoJson = JSONObject.parseObject(jdPayAggregateCreateOrderResponse.getPayInfo());
            if (payDto.getChannel().equals(ChannelEnum.JDAGGREGATE_ALIPAY.getCode())) {
                payBo.setPayLoad(payInfoJson.getString("tradeId"));
            } else {
                StringJoiner payInfo = new StringJoiner(",");
                for (String key : payInfoJson.keySet()) {
                    payInfo.add(key + "=" + payInfoJson.getString(key));
                }
                payBo.setPayLoad("{" + payInfo.toString() + "}");
            }

        } else {
            payBo.setPayLoad(jdPayAggregateCreateOrderResponse.getWebUrl());
        }
        return payBo;
    }

    /**
     * 订单关闭
     */
    @Override
    public OrderClosedBo orderClosed(OrderClosedDto orderClosedDto) throws PaymentException {
        String logPrefix = "orderClosed " + orderClosedDto.getPayNo();
        // 1、查询原订单
        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(orderClosedDto.getPayNo());
        if (null == prePayOrder) {
            log.info("{} 订单不存在", logPrefix);
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        // 2、查询支付结果
        List<PayResultOrder> payResultOrders = payResultOrderService.selectListByPayNo(orderClosedDto.getPayNo());
        if (!CollectionUtils.isEmpty(payResultOrders)) {
            log.info("{} 您的订单已支付完成，无法关闭 ", logPrefix);
            throw new PaymentException(ResultCodeEnum.ORDER_ALREADY_PAY_NO_CLOSED);
        }
        // 3、关单
        log.info("{} JDAggrateAdapter#orderClosed orderClosedDto={}", logPrefix, JSONUtils.toJSON(orderClosedDto));
        try {
            Object channelObj = JSONUtils.getValueFromJson(prePayOrder.getExtra(), "channel");
            String channel = channelObj == null ? "": (String) channelObj;
            JdPayAggregateCloseVo payAggrateCloseVo = JdPayAggregateCloseVo.builder().merchantNo(JDConstants.aggregateMerchant(logPrefix,channel, prePayOrder.getBusinessType())).orignalOutTradeNo(orderClosedDto.getPayNo()).build();
            jdAggregateAdpaterService.aggrateClose(logPrefix, payAggrateCloseVo);
        } catch (Exception e) {
            log.error("{} JDAdapter#orderClosed_error payDto={} error : {}", logPrefix, JSONUtils.toJSON(orderClosedDto), e);
            throw new PaymentException(ResultCodeEnum.ORDER_CLOSED_ERROR);
        }
        // 4、返回响应
        OrderClosedBo orderClosedBo = new OrderClosedBo();
        orderClosedBo.setPayNo(orderClosedDto.getPayNo());
        orderClosedBo.setStatus(OrderClosedBo.SUCCESS);
        return orderClosedBo;
    }

    /**
     * 退款
     */
    @Override
    public RefundBO refund(RefundDto refundDto) throws PaymentException {
        String logPrefix = "jdAggrateRefund " + refundDto.getPayNo();
        log.info("{} JDAggrateAdapter#refund refundDto={}", logPrefix, JSONUtils.toJSON(refundDto));
        // 发起退款
        JdPayAggregateRefundVo jdPayAggregateRefundVo = new JdPayAggregateRefundVo();
        jdPayAggregateRefundVo.setMerchantNo(JDConstants.aggregateMerchant(logPrefix,refundDto.getChannel(), refundDto.getBusinessType()));
        jdPayAggregateRefundVo.setOutTradeNo(refundDto.getRefundNo());
        jdPayAggregateRefundVo.setOriginalOutTradeNo(refundDto.getPayNo());
        jdPayAggregateRefundVo.setTradeAmount(String.valueOf(refundDto.getAmount()));
        jdPayAggregateRefundVo.setTradeDate(DateUtil.format(new Date(), DateUtil.YYYYMMDDHHMMSS));
        JdPayAggregateRefundResponse jdTradeRefundRequestVo;
        try {
            jdTradeRefundRequestVo = jdAggregateAdpaterService.aggrateRefund(logPrefix, jdPayAggregateRefundVo);
        } catch (PaymentException ep) {
            log.error("{} JDAggrateAdapter#refund request:{} ", logPrefix, JSON.toJSONString(refundDto), ep);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, ep.getErrMsg());
        } catch (Exception e) {
            log.error("{} JDAggrateAdapter#refund request:{} ", logPrefix, JSON.toJSONString(refundDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, e.getMessage());
        }
        // 返回数据
        RefundBO refundBo = new RefundBO();
        refundBo.setRefundNo(refundDto.getRefundNo());
        refundBo.setChannelRefundNo(jdTradeRefundRequestVo.getTradeNo());
        refundBo.setChannelTransactionNo(jdTradeRefundRequestVo.getOriginalOutTradeNo());
        refundBo.setStatus(RefundOrder.STATUS_IN);
        return refundBo;
    }

    /**
     * 检查交易状态
     */
    @Override
    public QueryResultBo queryTransactionStatus(QueryTransactionStatusDto queryTransactionStatusDto) throws PaymentException {
        String logPrefix = "jdAggrateQuery " + queryTransactionStatusDto.getPayNo();
        log.info("{} JDAggrateAdapter#queryTransactionInfo 入参信息：{} ", logPrefix, JSON.toJSONString(queryTransactionStatusDto));
        QueryResultBo queryResultBo = new QueryResultBo();
        try {
            // 查询支付状态
            if (QueryTransactionStatusDto.TRADE_TYPE_PAY.equals(queryTransactionStatusDto.getTradeType())) {
                JdPayAggregateQueryOrderVo request = JdPayAggregateQueryOrderVo.builder().merchantNo(JDConstants.aggregateMerchant(logPrefix,queryTransactionStatusDto.getPayChannel(), queryTransactionStatusDto.getBusinessType())).outTradeNo(queryTransactionStatusDto.getTransactionNo()).build();
                JdPayAggregateQueryOrderResponse response = jdAggregateAdpaterService.queryAggrateOrder(logPrefix, request);
                if (response.getTradeStatus().equals(JDPayTradeStatusEnum.FINI.getCode()) || response.getTradeStatus().equals(JDPayTradeStatusEnum.REFU.getCode())) {
                    queryResultBo.setStatus(QueryResultBo.FINISHED);
                    queryResultBo.setSuccessTime(DateUtil.strToLong(response.getFinishDate(), DateUtil.YYYYMMDDHHMMSS));
                    queryResultBo.setChannelChargeNo(response.getTradeNo());
                    queryResultBo.setAmount(Integer.valueOf(response.getTradeAmount()));
                } else if (response.getTradeStatus().equals(JDPayTradeStatusEnum.CLOS.getCode())) {
                    queryResultBo.setStatus(QueryResultBo.FAILED);
                    queryResultBo.setFailureCode(response.getResultCode());
                    queryResultBo.setFailureMsg(response.getResultDesc());
                } else {
                    queryResultBo.setStatus(QueryResultBo.PROCESSING);
                }
            }
            // 查询退款状态
            else if (QueryTransactionStatusDto.TRADE_TYPE_REFUND.equals(queryTransactionStatusDto.getTradeType())) {
                JdPayAggregateRefundQueryVo request = JdPayAggregateRefundQueryVo.builder().merchantNo(JDConstants.aggregateMerchant(logPrefix,queryTransactionStatusDto.getPayChannel(), queryTransactionStatusDto.getBusinessType())).outTradeNo(queryTransactionStatusDto.getTransactionNo()).build();
                JdPayAggregateRefundQueryResponse response = jdAggregateAdpaterService.queryAggrateRefund(logPrefix, request);
                if (response.getTradeStatus().equals(JDPayTradeStatusEnum.FINI.getCode()) || response.getTradeStatus().equals(JDPayTradeStatusEnum.REFU.getCode())) {
                    queryResultBo.setStatus(QueryResultBo.FINISHED);
                    queryResultBo.setSuccessTime(DateUtil.strToLong(response.getFinishDate(), DateUtil.YYYYMMDDHHMMSS));
                    queryResultBo.setChannelChargeNo(response.getTradeNo());
                    queryResultBo.setAmount(Integer.valueOf(response.getTradeAmount()));
                } else if (response.getTradeStatus().equals(JDPayTradeStatusEnum.CLOS.getCode())) {
                    queryResultBo.setStatus(QueryResultBo.FAILED);
                    queryResultBo.setFailureCode(response.getResultCode());
                    queryResultBo.setFailureMsg(response.getResultDesc());
                } else {
                    queryResultBo.setStatus(QueryResultBo.PROCESSING);
                }
            } else {
                log.info("{} JDAggrateAdapter#queryTransactionInfo交易类型错误：{} ", logPrefix, JSONObject.toJSONString(queryTransactionStatusDto));
                throw new PaymentException(ResultCodeEnum.PARAM_SUBJECT_ERROR);
            }
        } catch (PaymentException ep) {
            if ("00072".equals(ep.getErrCode()) || "RFND000004".equals(ep.getErrCode()) || "JMPT100029".equals(ep.getErrCode())) {
                log.info("{} 订单不存在，返回null，{}", logPrefix, JSON.toJSONString(queryTransactionStatusDto));
                return null;
            }
            log.error("{} JDAggrateAdapter#queryTransactionInfo error separateAccountDO:", logPrefix, JSON.toJSONString(queryTransactionStatusDto), ep);
            throw ep;
        } catch (Exception e) {
            log.error("{} JDAggrateAdapter#queryTransactionInfo error separateAccountDO:", logPrefix, JSON.toJSONString(queryTransactionStatusDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
        return queryResultBo;
    }

    /**
     * 查询退款状态
     */
    @Override
    public QueryResultBo queryRefundStatus(RefundOrder refundOrder) throws PaymentException {
        PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(refundOrder.getPayNo(), refundOrder.getChannel());
        QueryTransactionStatusDto queryTransactionStatusDto = QueryTransactionStatusDto.build(resultOrder.getBusinessType(), resultOrder.getBusinessOrderType(), refundOrder.getRefundNo(), QueryTransactionStatusDto.TRADE_TYPE_REFUND, getPayeeAccountId(refundOrder.getUserId()), refundOrder.getUpdateTime(), refundOrder.getPayNo(),resultOrder.getPayChannel());
        return queryTransactionStatus(queryTransactionStatusDto);
    }

    /**
     * 查询支付状态
     */
    @Override
    public QueryResultBo queryPayStatus(String tradeNo, PrePayOrder prePayOrder, String payMode) throws PaymentException {
        Object channelObj = JSONUtils.getValueFromJson(prePayOrder.getExtra(), "channel");
        String channel = channelObj == null ? "": (String) channelObj;
        QueryTransactionStatusDto queryTransactionStatusDto = QueryTransactionStatusDto.build(prePayOrder.getBusinessType(), prePayOrder.getBusinessOrderType(), prePayOrder.getPayNo(), QueryTransactionStatusDto.TRADE_TYPE_PAY, prePayOrder.getReceiverId(), prePayOrder.getCreateTime(), prePayOrder.getPayNo(),channel);
        return queryTransactionStatus(queryTransactionStatusDto);
    }

    /**
     * 获取收款人信息
     */
    private String getPayeeAccountId(String accountId) {
        if (StringUtils.isBlank(accountId)) {
            return accountId;
        }
        return isBigShop(accountId) ? ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_PINGAN_ACCOUNT_ID) : accountId;
    }

    /**
     * 判断是否为大商户
     */
    private boolean isBigShop(String accountId) {
        return Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), accountId) || Objects.equals(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_PINGAN_ACCOUNT_ID), accountId);
    }

}
